/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "serpent_shrine.h"
#include "Player.h"
#include "TemporarySummon.h"

DoorData const doorData[] =
{
	{ GO_LADY_VASHJ_BRIDGE_CONSOLE, DATA_BRIDGE_EMERGED, DOOR_TYPE_PASSAGE, BOUNDARY_NONE },
	{ GO_COILFANG_BRIDGE1, DATA_BRIDGE_EMERGED, DOOR_TYPE_PASSAGE, BOUNDARY_NONE },
	{ GO_COILFANG_BRIDGE2, DATA_BRIDGE_EMERGED, DOOR_TYPE_PASSAGE, BOUNDARY_NONE },
	{ GO_COILFANG_BRIDGE3, DATA_BRIDGE_EMERGED, DOOR_TYPE_PASSAGE, BOUNDARY_NONE }
};

class instance_serpent_shrine : public InstanceMapScript
{
public:
	instance_serpent_shrine() : InstanceMapScript("instance_serpent_shrine", 548) { }

	struct instance_serpentshrine_cavern_InstanceMapScript : public InstanceScript
	{
		instance_serpentshrine_cavern_InstanceMapScript(Map* map) : InstanceScript(map)
		{
		}

		void Initialize()
		{
			SetBossNumber(MAX_ENCOUNTERS);
			LoadDoorData(doorData);

			StrangePool = 0;
			LadyVashjGUID = 0;
			memset(&ShieldGeneratorGUID, 0, sizeof(ShieldGeneratorGUID));
			AliveKeepersCount = 0;
			LeotherasTheBlindGUID = 0;
			LurkerBelowGUID = 0;
		}

		bool SetBossState(uint32 type, EncounterState state)
		{
			if (!InstanceScript::SetBossState(type, state))
				return false;

			if (type == DATA_LADY_VASHJ)
			for (uint8 i = 0; i < 4; ++i)
			if (GameObject* gobject = instance->GetGameObject(ShieldGeneratorGUID[i]))
				gobject->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);

			return true;
		}

		void OnGameObjectCreate(GameObject* go)
		{
			switch (go->GetEntry())
			{
			case GO_LADY_VASHJ_BRIDGE_CONSOLE:
			case GO_COILFANG_BRIDGE1:
			case GO_COILFANG_BRIDGE2:
			case GO_COILFANG_BRIDGE3:
				AddDoor(go, true);
				break;
			case GO_SHIELD_GENERATOR1:
			case GO_SHIELD_GENERATOR2:
			case GO_SHIELD_GENERATOR3:
			case GO_SHIELD_GENERATOR4:
				ShieldGeneratorGUID[go->GetEntry() - GO_SHIELD_GENERATOR1] = go->GetGUID();
				break;
			}
		}

		void OnGameObjectRemove(GameObject* go)
		{
			switch (go->GetEntry())
			{
			case GO_LADY_VASHJ_BRIDGE_CONSOLE:
			case GO_COILFANG_BRIDGE1:
			case GO_COILFANG_BRIDGE2:
			case GO_COILFANG_BRIDGE3:
				AddDoor(go, false);
				break;
			}
		}

		void OnCreatureCreate(Creature* creature)
		{
			switch (creature->GetEntry())
			{
			case NPC_COILFANG_SHATTERER:
			case NPC_COILFANG_PRIESTESS:
				if (creature->GetPositionX() > -110.0f && creature->GetPositionX() < 155.0f && creature->GetPositionY() > -610.0f && creature->GetPositionY() < -280.0f)
					AliveKeepersCount += creature->IsAlive() ? 0 : -1; // retarded SmartAI calls JUST_RESPAWNED in AIInit...
				break;
			case NPC_THE_LURKER_BELOW:
				LurkerBelowGUID = creature->GetGUID();
				break;
			case NPC_LEOTHERAS_THE_BLIND:
				LeotherasTheBlindGUID = creature->GetGUID();
				break;
			case NPC_CYCLONE_KARATHRESS:
				creature->GetMotionMaster()->MoveRandom(50.0f);
				break;
			case NPC_LADY_VASHJ:
				LadyVashjGUID = creature->GetGUID();
				break;
			case NPC_ENCHANTED_ELEMENTAL:
			case NPC_COILFANG_ELITE:
			case NPC_COILFANG_STRIDER:
			case NPC_TAINTED_ELEMENTAL:
				if (Creature* vashj = instance->GetCreature(LadyVashjGUID))
					vashj->AI()->JustSummoned(creature);
				break;
			}
		}

		uint64 GetData64(uint32 identifier) const
		{
			switch (identifier)
			{
			case NPC_THE_LURKER_BELOW:
				return LurkerBelowGUID;
			case NPC_LEOTHERAS_THE_BLIND:
				return LeotherasTheBlindGUID;
			case NPC_LADY_VASHJ:
				return LadyVashjGUID;
			}
			return 0;
		}

		void SetData(uint32 type, uint32 data)
		{
			switch (type)
			{
			case DATA_PLATFORM_KEEPER_RESPAWNED:
				++AliveKeepersCount;
				break;
			case DATA_STRANGE_POOL:
				StrangePool = data;
				break;
			case DATA_PLATFORM_KEEPER_DIED:
				--AliveKeepersCount;
				break;
			case DATA_BRIDGE_ACTIVATED:
				SetBossState(DATA_BRIDGE_EMERGED, NOT_STARTED);
				SetBossState(DATA_BRIDGE_EMERGED, DONE);
				break;
			case DATA_ACTIVATE_SHIELD:
				if (Creature* vashj = instance->GetCreature(LadyVashjGUID))
				for (uint8 i = 0; i < 4; ++i)
				if (GameObject* gobject = instance->GetGameObject(ShieldGeneratorGUID[i]))
				{
					gobject->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
					vashj->SummonTrigger(gobject->GetPositionX(), gobject->GetPositionY(), gobject->GetPositionZ(), 0.0f, 0);
				}
				break;
			}
		}

		uint32 GetData(uint32 type) const
		{
			if (type == DATA_ALIVE_KEEPERS)
				return AliveKeepersCount;

			switch (type)
			{
			case DATA_STRANGE_POOL:
				return StrangePool;
			default:
				break;
			}

			return 0;
		}

		std::string GetSaveData()
		{
			OUT_SAVE_INST_DATA;

			std::ostringstream saveStream;
			saveStream << "S C " << GetBossSaveData();

			OUT_SAVE_INST_DATA_COMPLETE;
			return saveStream.str();
		}

		void Load(char const* str)
		{
			if (!str)
			{
				OUT_LOAD_INST_DATA_FAIL;
				return;
			}

			OUT_LOAD_INST_DATA(str);

			char dataHead1, dataHead2;

			std::istringstream loadStream(str);
			loadStream >> dataHead1 >> dataHead2;

			if (dataHead1 == 'S' && dataHead2 == 'C')
			{
				for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i)
				{
					uint32 tmpState;
					loadStream >> tmpState;
					if (tmpState == IN_PROGRESS || tmpState > SPECIAL)
						tmpState = NOT_STARTED;
					SetBossState(i, EncounterState(tmpState));
				}
			}
			else
				OUT_LOAD_INST_DATA_FAIL;

			OUT_LOAD_INST_DATA_COMPLETE;
		}

	private:
		uint64 LadyVashjGUID;
		uint64 ShieldGeneratorGUID[4];
		uint64 LurkerBelowGUID;
		uint64 LeotherasTheBlindGUID;
		//uint32 m_auiEncounter[MAX_ENCOUNTERS];
		uint32 StrangePool;
		int32 AliveKeepersCount;
	};

	InstanceScript* GetInstanceScript(InstanceMap* map) const
	{
		return new instance_serpentshrine_cavern_InstanceMapScript(map);
	}
};

class spell_serpentshrine_cavern_serpentshrine_parasite : public SpellScriptLoader
{
public:
	spell_serpentshrine_cavern_serpentshrine_parasite() : SpellScriptLoader("spell_serpentshrine_cavern_serpentshrine_parasite") { }

	class spell_serpentshrine_cavern_serpentshrine_parasite_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_serpentshrine_cavern_serpentshrine_parasite_AuraScript)

		void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			if (GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
				GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_SERPENTSHRINE_PARASITE, true);
		}

		void Register()
		{
			AfterEffectRemove += AuraEffectRemoveFn(spell_serpentshrine_cavern_serpentshrine_parasite_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_serpentshrine_cavern_serpentshrine_parasite_AuraScript();
	}
};

class spell_serpentshrine_cavern_serpentshrine_parasite_trigger : public SpellScriptLoader
{
public:
	spell_serpentshrine_cavern_serpentshrine_parasite_trigger() : SpellScriptLoader("spell_serpentshrine_cavern_serpentshrine_parasite_trigger") { }

	class spell_serpentshrine_cavern_serpentshrine_parasite_trigger_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_serpentshrine_cavern_serpentshrine_parasite_trigger_AuraScript)

		void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			if (GetTarget()->GetInstanceScript() && GetTarget()->GetInstanceScript()->IsEncounterInProgress())
				GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_SERPENTSHRINE_PARASITE, true);
		}

		void Register()
		{
			AfterEffectRemove += AuraEffectRemoveFn(spell_serpentshrine_cavern_serpentshrine_parasite_trigger_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_serpentshrine_cavern_serpentshrine_parasite_trigger_AuraScript();
	}

	class spell_serpentshrine_cavern_serpentshrine_parasite_trigger_SpellScript : public SpellScript
	{
		PrepareSpellScript(spell_serpentshrine_cavern_serpentshrine_parasite_trigger_SpellScript);

		void HandleApplyAura(SpellEffIndex effIndex)
		{
			PreventHitDefaultEffect(effIndex);
			if (Creature* target = GetHitCreature())
				target->DespawnOrUnsummon(1);
		}

		void Register()
		{
			OnEffectHitTarget += SpellEffectFn(spell_serpentshrine_cavern_serpentshrine_parasite_trigger_SpellScript::HandleApplyAura, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
		}
	};

	SpellScript* GetSpellScript() const
	{
		return new spell_serpentshrine_cavern_serpentshrine_parasite_trigger_SpellScript();
	}
};

class spell_serpentshrine_cavern_infection : public SpellScriptLoader
{
public:
	spell_serpentshrine_cavern_infection() : SpellScriptLoader("spell_serpentshrine_cavern_infection") { }

	class spell_serpentshrine_cavern_infection_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_serpentshrine_cavern_infection_AuraScript)

		void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && GetTarget()->GetInstanceScript())
			{
				CustomSpellValues values;
				values.AddSpellMod(SPELLVALUE_MAX_TARGETS, 1);
				values.AddSpellMod(SPELLVALUE_BASE_POINT0, aurEff->GetAmount() + 500);
				values.AddSpellMod(SPELLVALUE_BASE_POINT1, aurEff->GetAmount() + 500);
				GetTarget()->CastCustomSpell(SPELL_RAMPART_INFECTION, values, GetTarget(), TRIGGERED_FULL_MASK, NULL);
			}
		}

		void Register()
		{
			AfterEffectRemove += AuraEffectRemoveFn(spell_serpentshrine_cavern_infection_AuraScript::HandleEffectRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_serpentshrine_cavern_infection_AuraScript();
	}
};

class spell_serpentshrine_cavern_coilfang_water : public SpellScriptLoader
{
public:
	spell_serpentshrine_cavern_coilfang_water() : SpellScriptLoader("spell_serpentshrine_cavern_coilfang_water") { }

	class spell_serpentshrine_cavern_coilfang_water_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_serpentshrine_cavern_coilfang_water_AuraScript)

		void HandleEffectApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			if (InstanceScript* instance = GetUnitOwner()->GetInstanceScript())
			if (instance->GetBossState(DATA_THE_LURKER_BELOW) != DONE)
			if (instance->GetData(DATA_ALIVE_KEEPERS) == 0)
				GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SCALDING_WATER, true);
		}

		void HandleEffectRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			GetUnitOwner()->RemoveAurasDueToSpell(SPELL_SCALDING_WATER);
		}

		void CalcPeriodic(AuraEffect const* /*aurEff*/, bool& isPeriodic, int32& amplitude)
		{
			InstanceScript* instance = GetUnitOwner()->GetInstanceScript();
			if (!instance || instance->GetBossState(DATA_THE_LURKER_BELOW) == DONE)
				return;

			isPeriodic = true;
			amplitude = 8 * IN_MILLISECONDS;
		}


		void HandlePeriodic(AuraEffect const* aurEff)
		{
			PreventDefaultAction();
			InstanceScript* instance = GetUnitOwner()->GetInstanceScript();
			if (!instance || GetUnitOwner()->GetMapId() != 548)
			{
				SetDuration(0);
				return;
			}

			if (instance->GetBossState(DATA_THE_LURKER_BELOW) == DONE || instance->GetData(DATA_ALIVE_KEEPERS) == 0 || GetUnitOwner()->GetPositionZ() > -20.5f || !GetUnitOwner()->IsInWater())
				return;

			for (uint8 i = 0; i < 3; ++i)
				GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_FRENZY_WATER, true);
		}

		void Register()
		{
			AfterEffectApply += AuraEffectApplyFn(spell_serpentshrine_cavern_coilfang_water_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
			AfterEffectRemove += AuraEffectRemoveFn(spell_serpentshrine_cavern_coilfang_water_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);

			DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_serpentshrine_cavern_coilfang_water_AuraScript::CalcPeriodic, EFFECT_0, SPELL_AURA_DUMMY);
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_serpentshrine_cavern_coilfang_water_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_DUMMY);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_serpentshrine_cavern_coilfang_water_AuraScript();
	}
};

void AddSC_instance_serpentshrine_cavern()
{
	new instance_serpent_shrine();
	new spell_serpentshrine_cavern_serpentshrine_parasite();
	new spell_serpentshrine_cavern_serpentshrine_parasite_trigger();
	new spell_serpentshrine_cavern_infection();
	new spell_serpentshrine_cavern_coilfang_water();
}
